﻿using System.Data;
using System.Data.Objects;
using System.Linq;
using System;

namespace Halfpipe.Data.Repository
{
    public class EntityRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        private EntityKey CreateEntityKey(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            return Context.CreateEntityKey(Context.GetEntitySetName(entity.GetType()), entity);
        }

        public void Delete(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            Entities.DeleteObject(GetAttachedEntity(entity));
        }

        public void DeleteAll()
        {
            foreach (TEntity entity in Entities.ToList())
            {
                Entities.DeleteObject(entity);
            }
        }

        private TEntity GetAttachedEntity(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            if (!Context.EntityIsDetached(entity))
            {
                return entity;
            }
            var ek = CreateEntityKey(entity);
            return (TEntity)Context.GetObjectByKey(ek);
        }

        public TEntity Insert(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            Entities.AddObject(entity);
            return entity;
        }

        public IQueryable<TEntity> SelectAll()
        {
            return Entities;
        }

        public TEntity Update(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            if (Context.EntityIsDetached(entity))
            {
                object dbEntity = GetAttachedEntity(entity);
                Entities.ApplyCurrentValues(entity);
                return (TEntity)dbEntity;
            }
            // otherwise, it's already attached, and modifications are already tracked
            return entity;
        }

        private ObjectContext Context { get; set; }

        private ObjectSet<TEntity> Entities { get; set; }

        public EntityRepository(ObjectContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            Context = context;
            Entities = context.CreateObjectSet<TEntity>();
        }
    }
}
